﻿using System;
using System.Text;

namespace FuzzScript
{
    class  FuzzySet : Node, IDOMValueNode
    {
        /// <summary>
        /// 
        /// </summary>
   //     protected double m_dVariableLowerLimit;

    //    protected double m_dVariableUpperLimit;

        /// <summary>
        /// Maps a variable value to its discrete index equivalent
        /// </summary>
        /// <param name="p_dVal"></param>
        /// <returns></returns>
        //public int VariableToDiscreteIndex(double p_dVal)
        //{
        //    if (p_dVal > m_dVariableUpperLimit)
        //        p_dVal = m_dVariableUpperLimit;

        //    if (p_dVal < m_dVariableLowerLimit)
        //        p_dVal = m_dVariableLowerLimit;

        //    return (int)(((p_dVal - m_dVariableLowerLimit) /
        //        (m_dVariableUpperLimit - m_dVariableLowerLimit)) * (FuzzyVariable.Resolution));
        //}

        /// <summary>
        /// The fuzzy set definition
        /// </summary>
        protected double[] m_dSetDoM = new double[FuzzyVariable.Resolution];

        /// <summary>
        /// Constructor. Clears the set.
        /// </summary>
        public FuzzySet()
        {
            Clear();
        }

        public FuzzySet(string p_sName)//, double p_dLowerLimit, double p_dUpperLimit)
        {
            m_sName = p_sName;
            //m_dVariableLowerLimit = p_dLowerLimit;
            //m_dVariableUpperLimit = p_dUpperLimit;

            Clear();
        }


        internal void Clear()
        {
            for (int i = 0; i < m_dSetDoM.Length; i++)
            {
                m_dSetDoM[i] = 0;
            }
        }

        public override string ToString()
        {
            return "SET {" + Name + "}";
        }

        public override double Execute()
        {
            return m_dValue;
        }

        public override void AddSibling(INode p_SiblingNode)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Generates the equivalent c# code for this node. The fuzzy set node returns the definition of the set, thus
        /// returns code of the form:
        /// 
        ///     {DOM1, DOM2, ....., DOMn }
        ///     
        /// Assignment is carried out at variable level, as set name constists of both variable and name
        /// </summary>
        /// <returns></returns>
        public override string Generate()
        {
            StringBuilder sCode = new StringBuilder();

            sCode.Append("{");
            for (int i = 0; i < m_dSetDoM.Length; i++)
            {
                sCode.Append(m_dSetDoM[i].ToString());

                if (i != m_dSetDoM.Length - 1)
                    sCode.Append(",");
            }
            sCode.Append("}");

            return sCode.ToString();
        }

        internal void SetValue(int p_iDiscreteVal)
        {
            m_dValue = m_dSetDoM[p_iDiscreteVal];
#if DEBUG
            System.Console.WriteLine(m_sSetName + ":" + p_iDiscreteVal + ":" + Value);
#endif
        }

        private double m_dValue;

        public double DOMValue
        {
            get { return m_dValue; }
        }

        internal FuzzySet Clone()
        {
            FuzzySet set = new FuzzySet();
            set.m_sName = m_sName;
            set.m_dSetDoM = ((double[])(m_dSetDoM.Clone()));
            return set;
        }

        internal void And(FuzzySet p_Set)
        {
            for (int i = 0; i < FuzzyVariable.Resolution; i++)
            {
                m_dSetDoM[i] = Math.Min(m_dSetDoM[i], p_Set.m_dSetDoM[i]);
            }
        }

        internal void Or(FuzzySet p_Set)
        {
            for (int i = 0; i < FuzzyVariable.Resolution; i++)
            {
                m_dSetDoM[i] = Math.Max(m_dSetDoM[i], p_Set.m_dSetDoM[i]);
            }
        }

        internal void MaxMin(FuzzySet ClauseSet, double p_dDoM)
        {
            for (int i = 0; i < FuzzyVariable.Resolution; i++)
            {
                m_dSetDoM[i] = Math.Max(m_dSetDoM[i], Math.Min(ClauseSet.m_dSetDoM[i], p_dDoM));
            }
        }

        internal void SetDiscreteDoM(int p_dValueIndex, double p_dDoM)
        {
            m_dSetDoM[p_dValueIndex] = p_dDoM;
        }

        internal int GetValueIndex()
        {
            return CoGDefuzzification();
        }

        internal int CoGDefuzzification()
        {

            double fNumerator = 0;
            double fDenominator = 0;
            double fCrispVal = 0;

            for (int i = 0; i < FuzzyVariable.Resolution; i++)
            {
                fNumerator += (i * m_dSetDoM[i]);
                fDenominator += m_dSetDoM[i];
            }

            if (fDenominator > 0)
                fCrispVal = fNumerator / fDenominator;
            else
                fCrispVal = FuzzyVariable.Resolution / 2;

            return (int)Math.Round(fCrispVal);
        }
    }

    class TriangularFuzzySet : FuzzySet
    {
        private TriangularFuzzySet() { }

        internal TriangularFuzzySet(string p_sName,
            int a, int b, int c)
        {

            //m_dVariableLowerLimit = p_dVariableLowerLimit;
            //m_dVariableUpperLimit = p_dVariableUpperLimit;

            m_sName = p_sName;

            //double a = VariableToDiscreteIndex(p_dP1);
            //double b = VariableToDiscreteIndex(p_dP2);
            //double c = VariableToDiscreteIndex(p_dP3);

            double d = b - a;
            double e = c - b;

            for (int i = (int)a; i < c; i++)
            {
                m_dSetDoM[i] = Math.Max(Math.Min((i - a) / d, (c - i) / e), 0);
            }
        }
    }

    class RShoulderFuzzySet : FuzzySet
    {
        private RShoulderFuzzySet() { }

        internal RShoulderFuzzySet(string p_sName,
                double a, double b)
        {
            //m_dVariableLowerLimit = p_dVariableLowerLimit;
            //m_dVariableUpperLimit = p_dVariableUpperLimit;

            m_sName = p_sName;

            
            //double a = VariableToDiscreteIndex(p_dP1);
            //double b = VariableToDiscreteIndex(p_dP2);

            double d = b - a;


            for (int i = (int)a; i < FuzzyVariable.Resolution; i++)
            {
                m_dSetDoM[i] = Math.Min((i - a) / d, 1);
            }
        }
    }

    class LShoulderFuzzySet : FuzzySet
    {
        private LShoulderFuzzySet() { }

        internal LShoulderFuzzySet(string p_sName,
                double a, double b)
        {
            //m_dVariableLowerLimit = p_dVariableLowerLimit;
            //m_dVariableUpperLimit = p_dVariableUpperLimit;

            m_sName = p_sName;

            //double a = VariableToDiscreteIndex(p_dP1);
            //double b = VariableToDiscreteIndex(p_dP2);

            double d = b - a;

            for (int i = 0; i < b; i++)
            {
                m_dSetDoM[i] = Math.Max(Math.Min((b - i) / d, 1), 0);
            }
        }
    }
}